--- 'Monad' transformer for 'Maybe'
package frege.control.monad.trans.MaybeT where

import frege.control.monad.trans.MonadTrans
import frege.control.monad.trans.MonadIO

{-- 
    The parameterizable maybe monad, obtained by composing an arbitrary
    monad with the 'Maybe' monad.
    
    Computations are actions that may produce a value or fail.
    
    The 'return' function yields a successful computation, while (>>=)
    sequences two subcomputations, failing on the first error.
-}
data MaybeT m a = MaybeT { run :: m (Maybe a) }

--- Transform the computation inside a @MaybeT@.
mapMaybeT :: (m (Maybe a) -> n (Maybe b)) -> MaybeT m a -> MaybeT n b
mapMaybeT f mt = MaybeT $ f $ MaybeT.run mt

instance Functor m => Functor (MaybeT m) where
    fmap f = mapMaybeT (fmap (fmap f))

instance  Monad m => Alt (MaybeT m) where
      (<|>) = mplus

instance Monad m => Monad (MaybeT m) where
    pure mt = lift $ pure mt
    x >>= f = MaybeT do
        v <- MaybeT.run x
        case v of
            Nothing -> return Nothing
            Just y  -> MaybeT.run (f y)
            
instance Monad m => MonadFail (MaybeT m) where
    fail _ = MaybeT (return Nothing)            

instance Monad m =>  MonadPlus (MaybeT m) where
    mzero = MaybeT (return Nothing)
    mplus x y = MaybeT $ do
        v <- MaybeT.run x
        case v of
            Nothing -> MaybeT.run y
            Just _  -> return v

instance MonadTrans MaybeT where
    lift mt = MaybeT (liftM Just mt)

instance MonadIO m => MonadIO (MaybeT m) where
    liftIO io = lift (liftIO io)